/**
 *@description: MY_UTILS_ASSERT为诊断宏, 修改为相应平台的诊断, 若不使用则可定义为空
 *@description: USING_STACK 等, 若不使用则需要注释掉
 *@description: my_malloc, 和 my_free 定义为相应平台的内存分配
 *@author: Kalimdorsummer
 *@date: 2020-09-28 17:40:04
*/
#ifndef MY_UTILS_H__
#define MY_UTILS_H__

#ifdef __cplusplus
extern "C" {
#endif

#include <stdlib.h>
#include <string.h>
#include "stdint.h"
#include "stdbool.h"
#include <time.h>

#include "my_utils_support_macro.h"

#ifndef MY_UTILS_ASSERT
#define MY_UTILS_ASSERT(EX)   PT_ASSERT(EX)// do{}while(0)
#endif // MY_UTILS_ASSERT


#define USING_STACK
#define USING_FIFO
#define USING_CRC
#define USING_SIZEOF_SAFE	// 使用sizeof_safe, 会增大目标文件大小, 根据实际情况开启

#ifndef MY_UNUSED
#define MY_UNUSED                   __attribute__((unused))
#endif // MY_UNUSED
static bool MY_UNUSED maybe_ptr(void * ex, size_t ex_size)
{
	#define RAM_START              (0x20000000)
	#define RAM_SIZE               (96)
	#define RAM_END                (RAM_START + RAM_SIZE * 1024)
    unsigned int *p_val = (unsigned int *)ex;

    if (ex_size == sizeof(void *)){     // 大小和指针一样
        // 对于cortex平台, 变量都存放在0x20开头的内存地址, 因此如果*ex的值
		// 是这个区间, 那么很可能就是指针
        if ( *p_val >= RAM_START && *p_val < RAM_END){
			while(ex_size){};	// 若停在这里, 可强制ex_size变量为0退出, 查看是哪里出错
            return true;
        }
    }

    return false;
}


#ifdef __GNUC__

// 如果绝对不是指针, 返回true
#define     not_ptr(x)    ({      \
	bool ret = false;	\
	if ( __builtin_types_compatible_p(__typeof(x), int32_t) ){        \
		ret = true;                           \
	}else if ( __builtin_types_compatible_p(__typeof(x), uint32_t) ){         \
		ret = true;                          \
	}else if ( __builtin_types_compatible_p(__typeof(x), float ) ){                    \
		ret = true;                          \
	}else if ( __builtin_types_compatible_p(__typeof(x), long ) ){                    \
		ret = true;                          \
	}else if ( __builtin_types_compatible_p(__typeof(x), unsigned long ) ){                    \
		ret = true;                          \
	}else if ( __builtin_types_compatible_p(__typeof(x), wchar_t ) ){                    \
		ret = true;                          \
	}else if ( __builtin_types_compatible_p(__typeof(x), char[4] ) ){                    \
		ret = true;                          \
	}else if ( __builtin_types_compatible_p(__typeof(x), unsigned char[4] ) ){                    \
		ret = true;                          \
	}else if ( __builtin_types_compatible_p(__typeof(x), short[2] ) ){                    \
		ret = true;                          \
	}else if ( __builtin_types_compatible_p(__typeof(x), unsigned short[2] ) ){                    \
		ret = true;                          \
	}else if ( __builtin_types_compatible_p(__typeof(x), int[1] ) ){                    \
		ret = true;                          \
	}else if ( __builtin_types_compatible_p(__typeof(x), unsigned int[1] ) ){                    \
		ret = true;                          \
	}else if ( __builtin_types_compatible_p(__typeof(x), long[1] ) ){                    \
		ret = true;                          \
	}else if ( __builtin_types_compatible_p(__typeof(x), unsigned long[1] ) ){                    \
		ret = true;                          \
	}                                                  \
	ret;	\
})
#else
#define     not_ptr(x)		true
#endif

//============================= 以上需要配置 ==========================================

#ifndef ATTR_PACK
#define ATTR_PACK                   __attribute__((packed))
#endif

#define memset	do_not_use_memset
#define memcpy	do_not_use_memcpy
#define strstr	do_not_use_strstr

void * memcpy_safe(void *dst, const void *src, uint32_t count, uint32_t max_len);
char * strstr_safe ( const char * s1, const char * s2, uint32_t max_len );

void * memset_safe ( void * s, int c, uint32_t count, uint32_t max_len );
void * memclear ( void * s, uint32_t count );


#define RED_SEVERE          1
#define ORANGE_HIGH         2
#define YELLOW_ELEVATED     3
#define BLUE_GUARDED        4
#define GREEN_LOW           5

// 简单测试表达式
#define CHECK_SIMP(ex, ...)       \
do{                                         \
    if(!(ex)){  \
        my_utils_log("(%s) at func:%s line:%d | ", #ex, __FUNCTION__, __LINE__); \
        my_utils_log(__VA_ARGS__); \
        my_utils_log("\n"); \
    }   \
} while(0)

// 测试失败goto到本地的标签, 例如在函数末尾添加error:
#define CHECK_LOCAL(ex, label, ...)       \
do{                                         \
    CHECK_SIMP(ex, ##__VA_ARGS__);   \
    if(!(ex)){  \
        goto label;\
    }   \
} while(0)

typedef void (*check_handler_t)(const char *ex_string, int32_t alert_lvl, const char *func, uint32_t line);
extern void set_check_handler(check_handler_t  handler);
extern check_handler_t     check_handler;
// 测试失败调用应用程序提供处理错误的回调函数cb
#define CHECK_GLOBAL(ex, alert_lvl, handler, ...)       \
do{                                         \
    CHECK_SIMP(ex, ##__VA_ARGS__);   \
    set_check_handler(handler);             \
    check_handler(#ex, alert_lvl, __FUNCTION__, __LINE__);    \
} while(0)

#ifdef __GNUC__
// 更安全版, max_len必须为常数
#define MEMSET_S(s, c, count, max_len)  \
({     \
    MY_UTILS_ASSERT(__builtin_constant_p(max_len));   \
    memset_safe(s, c, count, max_len);      \
})

#define MEMCPY_S(dst, src, count, max_len)  \
({     \
    MY_UTILS_ASSERT(__builtin_constant_p(max_len));   \
    memcpy_safe(dst, src, count, max_len);      \
})

#define STRSTR_S(s1, s2, max_len)  \
({     \
    MY_UTILS_ASSERT(__builtin_constant_p(max_len));   \
    strstr_safe(s1, s2, max_len);      \
})
#else
#define MEMSET_S(s, c, count, max_len)          memset_safe(s, c, count, max_len)
#define MEMCPY_S(dst, src, count, max_len)      memcpy_safe(dst, src, count, max_len)
#define STRSTR_S(s1, s2, max_len)               strstr_safe(s1, s2, max_len)
#endif // __GNUC__

#ifdef __GNUC__
// 获取ex大小前先检测是否可能为指针, 对于sizeof(ptr), 可能不是开发者想要的
// C语言没有好的检查是指针的方法, 仅可根据CPU平台, 猜测到可能是指针
// 当传入的变量大小与(void *)一样时有可能是指针, 还需要该平台实现一个判断方法
// 该宏不可用于获取类型大小, 例如sizeof_safe(int), 编译会报错误
// 需要获取类型大小的话需要用预定义宏, 例如__sizeof_int
// 该宏会有所增加编译出的文件大小(每次调用约增大30字节), 若资源紧张, 需要关闭
#ifdef USING_SIZEOF_SAFE
#define sizeof_safe(ex)	\
({	\
	if (!not_ptr(ex)){	/*不一定不是指针*/ \
		maybe_ptr(&ex, sizeof(ex) );		\
	}	\
	sizeof(ex);	\
})
#else
#define sizeof_safe(ex)		sizeof(ex)
#endif

#else
#define sizeof_safe(ex)		sizeof(ex)
#endif

uint32_t reverse_byte(uint32_t val, uint8_t num);

bool DATE_TIME_to_tm(struct tm *p_tm, char *date, char *time_);


//================== 转换操作 ========================
unsigned char AscToHex ( char h, char l );
int StrToHex ( char * p_hex, char * p_str, int len, int hex_len );
int HexToChar ( unsigned char * p_char, unsigned char * p_hex, int len, int char_len );
int HexToStr ( unsigned char * p_str, unsigned char * p_hex, int len, int str_len );


//================== 转换操作 end ========================
// 测试位
static inline bool test_bit(uint32_t num, uint32_t bit_cnt)
{
	if(num & (1 << bit_cnt) ){
		return true;
	}

	return false;

}


//================= 栈操作 ========================
// 必须使用stack_create动态创建或者DEFINE_STACK静态定义, 静态定义需要调用statck_init初始化
#ifdef USING_STACK

#define STACK_STATUS_OK     0X0000
#define STACK_STATUS_ERR    0X00F0
#define STACK_PARAM_NULL    0X00F1
#define STACK_FULL          0X00A0
#define STACK_EMPTY         0X00B0


#define STACK_STRUCT_SIZE    ( sizeof(size_t) * 3 ) // fifo结构体的字节数
typedef struct stack_cover*	stack_t;	// 一层封皮, 用于检测输入参数类型是否匹配, 不匹配编译器可给出警告

stack_t stack_create(size_t stack_size, size_t element_size);
void stack_init(stack_t pv_s, size_t element_max, size_t element_size);
void stack_destroy(stack_t pv_s);
void stack_make_empty(stack_t pv_s);
bool stack_is_empty(stack_t pv_s);
bool stack_is_full(stack_t pv_s);
int32_t stack_push(stack_t pv_s, void *in);
int32_t stack_pop(stack_t pv_s, void *out);

// 静态定义stack, name直接是指针, 不需要&访问
// 为了使buf紧跟在stack_ins后, 打包定义
#define DEFINE_STACK(name, element_max, element_type)    \
struct name##stack_and_buf{               \
    uint32_t        stack_ins[STACK_STRUCT_SIZE / sizeof(uint32_t)];          \
    uint8_t         buf[element_max * sizeof(element_type)];    \
}ATTR_PACK;  \
struct name##stack_and_buf name##_stack_whole = {   \
    .stack_ins[0]   = 0,    \
    .stack_ins[1]   = element_max * sizeof(element_type),    \
    .stack_ins[1]   = sizeof(element_type),    \
};\
const stack_t name = (stack_t)&name##_stack_whole.stack_ins;


#define DECLARE_STACK(name) \
extern stack_t name;
#endif
//================= 栈操作 end ========================

//================= fifo 操作 =========================
// 必须使用fifo_create动态创建或者DEFINE_FIFO静态定义, 静态定义需要调用fifo_init初始化
#ifdef USING_FIFO
#define FIFO_STATUS_OK      0X00
#define FIFO_STATUS_ERR     0XF0
#define FIFO_PARAM_INVAL    0XF1    // 参数无效

#define FIFO_FULL           0XA0
#define FIFO_EMPTY          0XB0
#define FIFO_HALF           0XC0


#define FIFO_STRUCT_SIZE    ( sizeof(size_t) * 8) // fifo结构体的字节数

typedef struct fifo_cover*	fifo_t;	// 一层封皮, 用于检测输入参数类型是否匹配, 不匹配编译器可给出警告


int32_t fifo_init(fifo_t pv_fifo, size_t element_max, size_t element_size);
fifo_t fifo_create(size_t element_max, size_t element_size);
void fifo_destroy(fifo_t pv_fifo);
size_t fifo_putf(fifo_t  pv_fifo, const void * in_val);
size_t fifo_put(fifo_t pv_fifo, const void * in_val);
size_t fifo_get ( fifo_t  pv_fifo, void * out_val );
size_t fifo_writef(fifo_t pv_fifo, void const * p_data_in, size_t cnt);
size_t fifo_write(fifo_t  pv_fifo, void const * p_data_in, size_t cnt);
size_t fifo_read( fifo_t  pv_fifo, void * p_data_out, size_t cnt);

uint16_t get_clr_fifo_errno(fifo_t pv_fifo);
void clr_fifo_errno(fifo_t pv_fifo);
uint16_t get_fifo_errno(fifo_t pv_fifo);
uint32_t get_fifo_element_cnt(fifo_t pv_fifo);
void fifo_flush(fifo_t pv_fifo);
void fifo_peek(fifo_t pv_fifo, size_t index, void * out);

// 定义fifo, name直接是指针, 不需要&访问
// 定义后即可使用
#define DEFINE_FIFO(name, element_max, element_type)    \
struct name##fifo_and_buf{     \
    uint32_t        fifo_ins[FIFO_STRUCT_SIZE / sizeof(uint32_t)]; /*fifo_t隐藏起来了, 只能定义一块内存区域, 无法得知类型*/ \
    uint8_t         buf[element_max * sizeof(element_type)];    \
}ATTR_PACK;\
struct name##fifo_and_buf     name##_fifo_whole = { \
    .fifo_ins[0]        = sizeof(element_type) * element_max,  \
    .fifo_ins[1]        = 0,   \
    .fifo_ins[2]        = 0,   \
    .fifo_ins[3]        = element_max,  \
    .fifo_ins[4]        = sizeof(element_type), \
    .fifo_ins[5]        = 0, \
    .fifo_ins[6]        = 0, \
    .fifo_ins[7]        = FIFO_EMPTY, \
};\
const fifo_t name = (fifo_t)&name##_fifo_whole.fifo_ins;

#define DECLARE_FIFO(name) \
extern fifo_t name;
#endif
//================= fifo 操作 end =========================


//================== 常用内建函数宏定义 ========================
#ifdef __GNUC__
/**
 *	x中1的个数
 */
#define popcount(s) __builtin_popcount(s)

/**
 *	x中1的奇偶性。
 *	奇:1, 偶:0
 */
#define parity(i) __builtin_parity(i)

/**
 *	当前函数的第n级调用者的地址，用的最多的就是__builtin_return_address(0)，
 *	即获得当前函数的调用者的地址。
 *	注意，该函数实现是体系结构相关的，有些体系结构只实现了n=0的返回结果。
 */
#define return_addr(s) __builtin_return_address(s)

/**
 *	按字节翻转x，返回翻转后的结果。
 *	uint16_t __builtin_bswap16 (uint16_t x)
 *	uint32_t __builtin_bswap32 (uint32_t x)
 */
#define swap16(i) __builtin_bswap16(i)
#define swap32(i) __builtin_bswap32(i)

/**
 *	判断exp是否在编译时就可以确定其为常量，
 *	如果exp为常量，该函数返回1，否则返回0。
 *	如果exp为常量，可以在代码中做一些优化来减少处理exp的复杂度
 */
#define constant_p(exp) __builtin_constant_p(exp)

/**
 *	判断type1和type2是否是相同的数据类型，相同返回1，否则返回0。
 *	该函数不区分const/volatile这样的修饰符，即int和const int被认为是相同的类型
 */
#define types_cmp_p(t1, t2) __builtin_types_compatible_p(t1, t2)

#elif defined(__CC_ARM)

#define return_addr(s) 		__return_address()
#define swap32(i) 			__rev(i)

#endif // __GNUC__

//================== 常用内建函数宏定义 end ========================


//================== 通用小函数或宏 =============================
// 获取数组元素个数
#ifndef SIZEOF_ARR
    #define SIZEOF_ARR(arr)     ( (size_t)( sizeof_safe(arr) / sizeof(arr[0]) ) )
#endif

#ifdef __GNUC__
#ifndef MAX
#define MAX(x, y) ({            \
    __typeof(x) _x = x;         \
    __typeof(y) _y = y;         \
    (void) ( &_x == &_y );      \
    _x >_y ? _x : _y;           \
})
#endif // MAX

#ifndef MIN
#define MIN(x, y) ({            \
    __typeof(x) _x = x;         \
    __typeof(y) _y = y;         \
    (void) ( &_x == &_y );      \
    _x >_y ? _y : _x;           \
})
#endif // MIN
#else

#ifndef MAX
#define MAX(x,y)     ( (x) > (y)?(x):(y) )
#endif // MAX

#ifndef MIN
#define MIN(x,y)     ( (x) > (y)?(y):(x) )
#endif // MIN

#endif // __GNUC__

#ifndef __GNUC__

#ifndef SWAP
#define SWAP(a, b)  \
do{     \
    unsigned char temp[8];              \
    memcpy(temp, &a, sizeof(a));  \
    memcpy(&a, &b, sizeof(a));  \
    memcpy(&b, temp, sizeof(a));  \
    (void) ( &a == &b );    \
}while(0)
#endif // SWAP

#else

#ifndef SWAP
#define SWAP(a, b)  \
do{                                                 \
    __typeof(a)  temp;               \
    temp = a;   \
    a = b;      \
    b = temp;   \
    (void) ( &a == &b );  \
}while(0)
#endif // SWAP

#endif // __GNUC__

// 判断是否奇数
static inline bool is_odd(uint32_t x)
{
    return (x & 0x01);
}

static inline bool is_even(uint32_t x)
{
    return !is_odd(x);
}

void gen_bswap(void *p_val, size_t size);
//================== 通用小函数或宏 end =============================


//================== crc 校验 =======================================
uint16_t crc16_modbus(uint8_t *puchMsg, uint16_t usDataLen)  ;
//================== crc 校验 =======================================
#ifdef __cplusplus
}
#endif

#endif // MY_UTILS_H__

